home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1996 May: Tool Chest / Developer CD Series May 1996 (Tool Chest) (Apple Computer) (1996).iso / Sample Code / System 7.0 Samples / AEObject-Edition1.0.2 Sample / AppleEventCore.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-11-18  |  26.3 KB  |  656 lines  |  [TEXT/MPS ]

  1. /*------------------------------------------------------------------------------
  2. *
  3. *  Apple Developer Technical Support
  4. *
  5. *  AppleEvent specific routines
  6. *
  7. *  Program:    AEObject-Edition Sample
  8. *  File:       AppleEventCore.c - C Source
  9. *
  10. *  by:         C.K. Haun <TR>
  11. *
  12. *  Copyright © 1990-1992 Apple Computer, Inc.
  13. *  All rights reserved.
  14. *
  15. *------------------------------------------------------------------------------
  16. * This file handles the generic AppleEvent handlers, as well as intialization
  17. * and installing the handlers I'll need for this application.
  18. * Also installs my coercion handlers and has the interaction Idle proc.
  19. * Other, more specific AE handling (like edition functions) are in the functional 
  20. * area files.
  21. * Functions that deal specifically with AppleEvent™ Objects are in the 
  22. * file AEObject.c.
  23. *----------------------------------------------------------------------------*/
  24. /* AESample v1: Removed all ShowMe error notifications, replaced with macro to display */
  25. /* errors non-intrusively in AESatus window */
  26.  
  27. #define __AEM__
  28.  
  29. #pragma segment Main
  30. /* DoHighLevel will be in main, just because */
  31. #include "Sampdefines.h"
  32.  
  33.  
  34.  
  35. /* DoHighLevel processes our high level events */
  36. /* All the HLEs that come into my application get dispatched to here. */
  37. /* All I do is call AEProcessAppleEvent, because I do not support any */
  38. /* other types of High Level Events, justs AEs. */
  39. void DoHighLevel(EventRecord AERecord)
  40. {
  41.     OSErr fred2;
  42.     /* Process the event. */
  43.     /* Here's what happens; 
  44.     1) If this is an AppleEvent, and we have a handler installed for this event, 
  45.     AEProcessAppleEvent jumps to that handler 
  46.     else
  47.     2) If there is a system handler for this event, AEProcessAppleEvent jumps to that
  48.     else
  49.     3) return errEventNotHandled.
  50.     */
  51.     /* •••• NOTE: If you are sending AppleEvents to yourself (using a PSN of kCurrentProcess */
  52.     /* in the address AEDesc to AECreateAppleEvent) then events sent to yourself that */
  53.     /* way will NOT come here!  They will be dispatched directly to the  */
  54.     /* correct handler by the AppleEvent manager!  Try it in this sample. */
  55.     fred2 = AEProcessAppleEvent(&AERecord);
  56.     if ((fred2 != userCanceledErr) && (fred2 != noErr) && (fred2 != errAEEventNotHandled))
  57.         mAEErrorDisplay("\pAppleEvent Proccessing.", fred2)
  58.         /* if it was a userCanceledErr (from the quit routine) and the reply has been */
  59.         /* sent from there already */
  60.         /* if it's a errAEEventNotHandled, then someone sent us  an event we're not prepared for, */
  61.         /* that is not a reason to put up a dialog.  Since anyone can send us events, then */
  62.         /* we could get all kinds of stray stuff, so just ignore it. */
  63.         /* you may want to check for debugging reasons */        
  64. }
  65.  
  66. /* end DoHighLevel */
  67.  
  68. #pragma segment AppleEvents
  69. /***************************************************************************************
  70.  
  71. MissedAnyParameters
  72.  
  73. Used to check for any unread required parameters. Returns true if we missed at
  74. least one.
  75.  
  76. *****************************************************************************************/
  77. Boolean MissedAnyParameters(AppleEvent *message)
  78. {
  79.     OSErr err;
  80.     DescType ignoredActualType;
  81.     AEKeyword missedKeyword;
  82.     Size ignoredActualSize;
  83.     EventRecord event;
  84.     
  85.     err = AEGetAttributePtr(message, keyMissedKeywordAttr, typeKeyword, &ignoredActualType, (Ptr)&missedKeyword,
  86.                             sizeof(missedKeyword), &ignoredActualSize);
  87.     
  88.     /* no error means that we found some more.*/
  89.     
  90.     if (!err) {
  91.         event.message = *(long *)&ignoredActualType;
  92.         event.where = *(Point *)&missedKeyword;
  93.         mAEErrorDisplay("\pMissedAnyParameters: got parameters I don't know what to do with.", err)
  94.         err = errAEEventNotHandled;
  95.     }
  96.     
  97.     /* errAEDescNotFound means that there are no more parameters. If we get */
  98.     /* an error code other than that, flag it. */
  99.     
  100.     else if (err != errAEDescNotFound) {
  101.         mAEErrorDisplay("\pMissedAnyParameters: after AEGetAttributeDesc.", err)
  102.         
  103.     }
  104.     return(err != errAEDescNotFound);
  105. }
  106.  
  107. /* This is the standard Open Application event.  You'll get this as one of the (if not the ) */
  108. /* first events in your application.  So, we open up a blank document */
  109. /* You will _NOT_ get this if you were launched with an event (like an 'odoc' or 'pdoc' ) */
  110. /* so do _not_ do application initialiaztion things here!  This routine may never */
  111. /* get called! */
  112. pascal OSErr AEOpenHandler(AppleEvent *messagein, AppleEvent *reply, long refIn)
  113. {
  114.     OSErr myErr = noErr;
  115. #pragma unused (messagein,refIn)
  116.     gCurrentReply = reply;
  117.  
  118.     ChangePlane(AddNewWindow(true));                        /* open our initial, blank window */
  119.     /* and make sure it's the front window */
  120.     
  121.     mAEErrorDisplay("\pOpen App Handler", myErr)
  122.     gCurrentReply = nil;
  123.     return(myErr);
  124. }
  125.  
  126. /* end AEOpenHandler */
  127.  
  128. /* Open Doc, opens our documents.  Remember, this can happen at application start AND */
  129. /* anytime else.  If your app is up and running and the user goes to the desktop, hilites one */
  130. /* of your files, and double-clicks or selects Open from the finder File menu this event */
  131. /* handler will get called. Which means you don't do any initialization of globals here, or */
  132. /* anything else except open then doc.  */
  133. /* SO -- Do NOT assume that you are at app start time in this */
  134. /* routine, or bad things will surely happen to you. */
  135.  
  136. pascal OSErr AEOpenDocHandler(AppleEvent *messagein, AppleEvent *reply, long refIn)
  137. {
  138.     OSErr myErr = noErr;
  139. #pragma unused ( refIn)
  140.     gCurrentReply = reply;
  141.  
  142.     /* In previous versions I was coming to the front on this message.  This is _not_ */
  143.     /* the proper thing to do.  I have changed things to call AEInteractWithUser.  */
  144.     /*  The user (or the AEm will bring us forward if appropriate).  But we may _not_ be  */
  145.     /*required to come forward, it may be perfectly reasonable to stay in the background */
  146.     /* so don't do anything unless you really need to, like to put up a dialog */
  147.     /* which we don't have to, so just press on */
  148.     myErr = processOpenPrint(messagein, false);
  149.     gCurrentReply = nil;
  150.     return(myErr);
  151. }
  152.  
  153. /* Same logic as OpenDoc */
  154. pascal OSErr AEPrintHandler(AppleEvent *messagein, AppleEvent *reply, long refIn)
  155. {                                                           /* no printing handler in yet, so we'll ignore this */
  156.     /* the operation is functionally identical to the ODOC event, with the additon */
  157.     /* of calling your print routine.  */
  158. #pragma unused (refIn)
  159.     gCurrentReply = reply;
  160.     processOpenPrint(messagein, true);
  161.     gCurrentReply = nil;
  162.     return(noErr);
  163. }
  164.  
  165. /* Standard Quit event handler, to handle a Quit event from the Finder, for example.  */
  166. /* ••••• DO NOT CALL EXITTOSHELL HERE ••••• or you will never have a happy life.  */
  167. /* OK, it's a few months after I wrote that comment, and I've seen a lot of code */
  168. /* come through DTS that calls ExitToShell from quit handlers.  Let me explain... */
  169. /* When an AppleEvent Handler is called (like this quit handler) you are ALMOST */
  170. /* 100% in your application world.  A5 is right, you can call any toolbox function, */
  171. /* you can call your own routines, everything _seems_ like you are in complete  */
  172. /* control.  Well, almost but not quite.  The routine has been dispatch to from the */
  173. /* AppleEvent Manager's space, so you _must_ return to that at some point! */
  174. /* Which is why you can't call ETS from here.  When you call ExitToShell from an */
  175. /* AE Handler, the most likely thing that happens is the FInder quits, and your  */
  176. /* application keeps running.  Which ain't what you want, y'know? */
  177. /* so, DON'T CALL EXITTOSHELL FROM AN APPLEEVENT HANDLER!!!!!!!!!!!!!! */
  178. pascal OSErr AEQuitHandler(AppleEvent *messagein, AppleEvent *reply, long refIn)
  179. {
  180. #pragma unused (messagein,refIn)
  181.     OSErr theErr;
  182.     Str32 userCanx;
  183.     gCurrentReply = reply;
  184.     theErr = PrepQuit();                                    /* prepQuit sets the Stop flag for us.  It does _NOT_ quit, you */
  185.     /* should NEVER quit from an AppleEvent handler.  Calling */
  186.     /* ExitToShell here would blow things up */
  187.     
  188.     if (!theErr){
  189.     gCurrentReply = nil;
  190.         return(noErr);}
  191.     if (theErr == (userCanceledErr)) {
  192.         /* reply to the application that told us to quit */
  193.         GetIndString(userCanx,kGeneralStrings,kUCanxString);
  194.         AEPutParamPtr(reply, keyErrorNumber, typeLongInteger, (Ptr)&theErr, sizeof(OSErr));
  195.         AEPutParamPtr(reply, keyErrorString, typeChar, (Ptr)&userCanx, userCanx[0]);
  196.     }
  197.     gCurrentReply = nil;
  198.         return(theErr);
  199.     
  200. }
  201.  
  202. /* This is the 'ansr', or answer, handler.  You need this if you ever want to */
  203. /* use QueueReply as an option to AESend (which you will) since the reply  */
  204. /* is going to be coming in through your event loop like any other  */
  205. /* AppleEvent.   So here it is */
  206. pascal OSErr AEAnswerHandler(AppleEvent *messagein, AppleEvent *reply, long refIn)
  207. {
  208. #pragma unused (refIn) 
  209.     OSErr myErr = noErr;
  210.         gCurrentReply = reply;
  211.     mVerboseOutput("\p\nGot an answer from an event")
  212.     DisplayReturnedData((AEDesc *)messagein);
  213.     gCurrentReply = nil;
  214.     return(myErr);
  215.     /* •••• NOTE:  One thing you may want to consider, and it's a side-effect of */
  216.     /* the AppleEvent manager's direct dispatching of events if you send them to yourself */
  217.     /* using the kCurrentProcess Process Serial Number constant. */
  218.     /* If you use kCurrentProcess, then the AppleEvent manager will directly dispatch */
  219.     /* to the AppleEvent handler, the event will _not_ go through WaitNextEvent. */
  220.     /* Normally, that's a very good thing.  But there is one potential problem.... */
  221.     /* It means that kQueueReply doesn't quite work the way you think it will when sending to */
  222.     /* yourself.  The reply will _not_ be put in the event queue, instead this Answer hander */
  223.     /* will be called directly.  And if you reply from the answer handler, that will dispatch right  */
  224.     /* back to itself.  And so on.  So be aware that Queued replies are _not_ really  */
  225.     /* queued when you are using kCurrentProcess! */
  226. }
  227.  
  228.  
  229. /* my routine to snatch a section handle out of an AppleEvent */
  230. OSErr GetSectionHandleFromEvent(AppleEvent *AEin, SectionHandle *theSection)
  231. {
  232.     DescType thisType;
  233.     Size returnedSize;
  234.     return(AEGetParamPtr(AEin, keyDirectObject, typeSectionH, &thisType, (Ptr)theSection, sizeof(theSection), &returnedSize));
  235. }
  236.  
  237. /* processOpenPrint handles ODOC and PDOC events.  Both events open a document, one prints it */
  238. OSErr processOpenPrint(AppleEvent *messagein, Boolean printIt)
  239. {
  240.     OSErr err;
  241.     OSErr err2;
  242.     AEDesc theDesc;
  243.     FSSpec theFSS;
  244.     register qq;
  245.     long numFilesToOpen;
  246.     AEKeyword ignoredKeyWord;
  247.     DescType ignoredType;
  248.     Size ignoredSize;
  249.     WindowPtr tWind;
  250.     err = AEGetParamDesc(messagein, keyDirectObject, typeAEList, &theDesc);
  251.     if (err) {
  252.         mAEErrorDisplay("\pGetParamDesc error in Open/Print", err)
  253.     }
  254.     if (!MissedAnyParameters(messagein)) {
  255.         
  256.         /* Got all the parameters we need. Now, go through the direct object, */
  257.         /* see what type it is, and parse it up. */
  258.         
  259.         if (err = AECountItems(&theDesc, &numFilesToOpen)) {
  260.             mAEErrorDisplay("\pAECountItems error in Open/Print", err)
  261.         } else {
  262.             for (qq = 1; ((qq <= numFilesToOpen) && (!err)); ++qq) {
  263.                 if (err = AEGetNthPtr(&theDesc, qq, typeFSS, &ignoredKeyWord, &ignoredType, (Ptr)&theFSS, sizeof(theFSS),
  264.                                       &ignoredSize)) {
  265.                     mAEErrorDisplay("\pAEGetNthPtr in Open/Print", err)
  266.                     
  267.                 } else {
  268.                     FInfo fileInfo;
  269.                     FSpGetFInfo(&theFSS, &fileInfo);        /* make sure it's a data file */
  270.                     if (fileInfo.fdCreator == kMySignature && fileInfo.fdType == kMyDocumentFileType)
  271.                         tWind = OpenFile(&theFSS);
  272.                     else
  273.                         tWind = nil;                        /* they may have double clicked on the prefs file */
  274.                 }
  275.                 if (printIt && tWind != nil) {
  276.                     
  277.                     PrintIt(tWind, false);                  /* in Print.c.  Does not yet print, but the idea is there */
  278.                     CloseMyWindow(tWind);
  279.                 }
  280.             }                                               /* for qq = ... */
  281.         }                                                   /* AECountItems OK */
  282.     }                                                       /* Got all necessary parameters */
  283.     
  284.     if (err2 = AEDisposeDesc(&theDesc)) {
  285.         mAEErrorDisplay("\pAEDisposeDesc in Open/Print", err2)
  286.     }
  287.     return(err ? err : err2);
  288. }
  289.  
  290. /* This is an AppleEvent Idle function.  It can be passed to AEInteractWithUser */
  291. /* or AESend. */
  292. /* What it does is this; */
  293. /* If some AppleEvent thing is taking a really long time, the AppleEvent manager */
  294. /* will call WaitNextEvent FOR you (yeah, yeah, only one event loop per app, oh well) */
  295. /* and give you a result (limited) if you need to do something.  That something */
  296. /* will only be an update,activate,OSevent, or null event */
  297. pascal Boolean CommonIdleFunction(EventRecord *whatEvent, long *sleeping, RgnHandle *mouseRgn)
  298. {
  299.     extern Boolean gInBackground;
  300.     
  301.     extern RgnHandle mousergn;
  302.     switch (whatEvent->what) {
  303.         case updateEvt:
  304.             DrawIt((WindowPtr)whatEvent->message);          /* draw whatever window needs an update */
  305.             
  306.             break;
  307.         case activateEvt:
  308.             if (whatEvent->modifiers & activeFlag)
  309.                 DrawIt((WindowPtr)whatEvent->message);
  310.             break;
  311.         case app4Evt:                                       /* or kOSEvent, I'm old fashioned */
  312.             switch ((whatEvent->message >> 24) & 0x0FF) {       /* high byte of message */
  313.                 case suspendResumeMessage:                  /* suspend/resume is also an activate/deactivate */
  314.                     gInBackground = (whatEvent->message & kResumeMask) == 0;
  315.                     if (gInBackground) {
  316.                         SpitClip();                         /* export the clipboard please */
  317.                         /* deactivate the current TextEdit record as necessary */
  318.                         if (FrontWindow() != nil) {
  319.                             windowCHandle tempCH = (windowCHandle)GetWRefCon(FrontWindow());
  320.                             if ((*tempCH)->boxHandle != nil)
  321.                                 TEDeactivate((*tempCH)->boxHandle);
  322.                         }
  323.                     } else {
  324.                         if (FrontWindow() != nil) {
  325.                             windowCHandle tempCH = (windowCHandle)GetWRefCon(FrontWindow());
  326.                             if ((*tempCH)->boxHandle != nil)
  327.                                 TEActivate((*tempCH)->boxHandle);
  328.                         }
  329.                     }
  330.                     break;
  331.             }
  332.             break;
  333.         case nullEvent:
  334.             AdjustCursor(whatEvent->where, mousergn);
  335.             *sleeping = 20;                                  /* may as well sleep a while */
  336.             *mouseRgn = mousergn;
  337.             break;
  338.      
  339.     }
  340.     return(false);                                          /* If it takes for-ever I will wait, for you...  Sorry. */
  341. }
  342.  
  343. AEIdleUPP gCommonIdleFunctionUPP = NewAEIdleProc(CommonIdleFunction);
  344.  
  345. /* This function sends a GetData AppleEvent to someone, and displays the */
  346. /* results in the AEStatus window */
  347. void SendGetData(short which)
  348. {
  349.     AEDesc thisAddress;
  350.     AppleEvent theEvent;
  351.     AEDesc reply;
  352.     OSErr myErr = noErr;
  353.     /* first make an address */
  354.     MakeAddress(&thisAddress);
  355.     myErr = AECreateAppleEvent(kAECoreSuite, kAEGetData, &thisAddress, kAutoGenerateReturnID, kAnyTransactionID, &theEvent);
  356.     myErr = BuildGetDataEvent(&theEvent, which);            /* in AEObjects.c */
  357.     if (!myErr) {
  358.         /* reply may or may not be used, depending on the reply mode you have set */
  359.         myErr = DoSend(&theEvent, &reply);
  360.         if (!myErr) {
  361.             /* display the results, if we were in WaitReply */
  362.             if (gReplyMode == 1)
  363.                 DisplayReturnedData(&reply);
  364.         }
  365.     }
  366. }
  367. /* This function sends a SetData AppleEvent to someone, and displays the */
  368. /* results in the AEStatus window */
  369. void SendSetData(short which)
  370. {
  371.     AEDesc thisAddress;
  372.     AppleEvent theEvent;
  373.     AEDesc reply;
  374.     OSErr myErr = noErr;
  375.     /* first make an address */
  376.     MakeAddress(&thisAddress);
  377.     myErr = AECreateAppleEvent(kAECoreSuite, kAESetData, &thisAddress, kAutoGenerateReturnID, kAnyTransactionID, &theEvent);
  378.     myErr = BuildSetDataEvent(&theEvent, which);            /* in AEObjects.c */
  379.     if (!myErr) { 
  380.         DoSend(&theEvent, &reply);
  381.     }
  382. }
  383.  
  384.  
  385. /* Dialog box handler for the Interaction Settings you want to use for this sample */
  386. void SetInteractionLevels(void)
  387. {
  388.     /* call my common start routine and preset some buttons */
  389.     DialogPtr tdial = CommonDStart(kInteractionDialog, gLocalInteraction + 6, gAESendInteraction + 9);
  390.     short hitItem = 0;
  391.     /* save some stuff in case the user cancels */
  392.     short saveMode1 = gLocalInteraction;
  393.     short saveMode2 = gAESendInteraction;
  394.     short saveMode3 = gAESwitchLayer;
  395.     
  396.     /* one additial item to set */
  397.     SetControlValue(SnatchHandle(tdial, kSwitchLItem), gAESwitchLayer);
  398.     
  399.     /* run the dialog */
  400.     {
  401.         ModalFilterUPP upp = NewModalFilterProc(standardDialogFilter);
  402.     
  403.         while (hitItem != ok && hitItem != cancel) {
  404.             ModalDialog(upp, &hitItem);
  405.             switch (hitItem) {
  406.                 case kSelfIItem:
  407.                 case kLocalIItem:
  408.                 case kAllIItem:
  409.                     /* check boxes, just toggle them */
  410.                     SetControlValue(SnatchHandle(tdial, gLocalInteraction + kSelfIItem), false);
  411.                     gLocalInteraction = hitItem - kSelfIItem;
  412.                     SetControlValue(SnatchHandle(tdial, gLocalInteraction + kSelfIItem), true);
  413.                     break;
  414.                     
  415.                 case kNeverIItem:
  416.                 case kCanIItem:
  417.                 case kAlwaysIItem:
  418.                     /* more toggleable checkboxes */
  419.                     SetControlValue(SnatchHandle(tdial, gAESendInteraction + kNeverIItem), false);
  420.                     gAESendInteraction = hitItem - kNeverIItem;
  421.                     SetControlValue(SnatchHandle(tdial, gAESendInteraction + kNeverIItem), true);
  422.                     
  423.                     break;
  424.                 case kSwitchLItem:
  425.                     gAESwitchLayer = (gAESwitchLayer ? false : kAECanSwitchLayer);
  426.                     SetControlValue(SnatchHandle(tdial, kSwitchLItem), gAESwitchLayer);
  427.                     break;
  428.                 default:
  429.                     break;
  430.             }
  431.         }
  432.         DisposeDialog(tdial);
  433.         DisposeRoutineDescriptor(upp);
  434.     }
  435.     
  436.     if (hitItem == cancel) {
  437.         /* if they canceled, restore the old values */
  438.         gLocalInteraction = saveMode1;
  439.         gAESendInteraction = saveMode2;
  440.         gAESwitchLayer = saveMode3;
  441.         
  442.     } else {
  443.     /* set interaction levels to what the user picked  */
  444.     AESetInteractionAllowed(gLocalInteraction);
  445.     }
  446. }
  447.  
  448. /* Dialog box handler for the addressing mode you want to use for this sample */
  449. void SetTargetAddress(void)
  450. {
  451.     /* bring up dialog and preset some stuff */
  452.     DialogPtr tdial = CommonDStart(kAddressingDialog, gAddressMode + kSelfAddressCurrItem, 0);
  453.     short hitItem = 0;
  454.     short saveMode = gAddressMode;
  455.     /* if an address is already specified, draw it in the box */
  456.     ParamText(&targetName, "", "", "");
  457.     /* dim out the 'Select Target' button if the right check box is not set */
  458.     if (gAddressMode + kSelfAddressCurrItem != kOtherAppItem)
  459.         HiliteControl(SnatchHandle(tdial, kSelectProcItem), 255);       /* can't find the constant for dimmed, somebody tell me where it is,please */
  460.  
  461.     {
  462.         ModalFilterUPP upp = NewModalFilterProc(standardDialogFilter);
  463.         /* run the dialog */
  464.         while (hitItem != ok && hitItem != cancel) {
  465.             ModalDialog(upp, &hitItem);
  466.             switch (hitItem) {
  467.                 case kSelfAddressCurrItem:
  468.                 case kSelfAddressPSNItem:
  469.                 case kOtherAppItem:
  470.                     /* checkbox toggling, and also setting the hilite state of the 'Set Target' */
  471.                     /* button as necessary */
  472.                     SetControlValue(SnatchHandle(tdial, gAddressMode + kSelfAddressCurrItem), false);
  473.                     gAddressMode = hitItem - kSelfAddressCurrItem;
  474.                     SetControlValue(SnatchHandle(tdial, gAddressMode + kSelfAddressCurrItem), true);
  475.                     if (gAddressMode + kSelfAddressCurrItem != kOtherAppItem)
  476.                         HiliteControl(SnatchHandle(tdial, kSelectProcItem), 255);
  477.                     else
  478.                         HiliteControl(SnatchHandle(tdial, kSelectProcItem), 0);
  479.                     break;
  480.                 case kSelectProcItem:
  481.                     /* Call the PPC browser to select a target */
  482.                     BrowseForTarget(&gTargetAddress);
  483.                     break;
  484.                 default:
  485.                     break;
  486.             }
  487.         }
  488.         DisposeRoutineDescriptor(upp);
  489.     }
  490.     DisposeDialog(tdial);
  491.     /* restore old mode if they canceled */
  492.     if (hitItem == cancel)
  493.         gAddressMode = saveMode;
  494.     
  495. }
  496.  
  497. /* Dialog box handler for the Reply Settings you want to use for this sample */
  498. void SetReplyMode(void)
  499. {
  500.     DialogPtr tdial = CommonDStart(kReplyModeDialog, gReplyMode + kNoReplyItem, 0);
  501.     short hitItem = 0;
  502.     short saveMode = gReplyMode;
  503.     
  504.     /* set up to gray out the 'no reply' button */
  505.     HiliteControl(SnatchHandle(tdial, kNoReplyItem), 255);
  506.     
  507.     {
  508.         ModalFilterUPP upp = NewModalFilterProc(standardDialogFilter);
  509.         while (hitItem != ok && hitItem != cancel) {
  510.             ModalDialog(upp, &hitItem);
  511.             switch (hitItem) {
  512.                 case kNoReplyItem:
  513.                 case kWaitReplyItem:
  514.                 case kQueueReplyItem:
  515.                     SetControlValue(SnatchHandle(tdial, gReplyMode + kNoReplyItem), false);
  516.                     gReplyMode = hitItem - kNoReplyItem;
  517.                     SetControlValue(SnatchHandle(tdial, gReplyMode + kNoReplyItem), true);
  518.                     break;
  519.                 default:
  520.                     break;
  521.             }
  522.         }
  523.         DisposeRoutineDescriptor(upp);
  524.     }
  525.     
  526.     DisposeDialog(tdial);
  527.     if (hitItem == cancel)
  528.         gReplyMode = saveMode;
  529. }
  530.  
  531.  
  532. /*  CoerceBooleanToChar creates a desc that says True or False.  It's not a very */
  533. /* interesting coercion, but it's a nice sample.  I use it for my output window */
  534. pascal OSErr CoerceBooleanToChar(DescType origData, Ptr inPtr, Size theSize, DescType toType, long refCon, AEDesc *result)
  535. {
  536. #pragma unused (refCon)
  537.     OSErr myErr = noErr;
  538.     /* make sure everything is fine first */
  539.     if (origData != typeBoolean || toType != typeChar) {
  540.         /* something is goofy here */
  541.         myErr = errAECoercionFail;
  542.     } else {
  543.         /* a boolean should be two bytes. if it isn't, I'm confused */
  544.         if (theSize == sizeof(short)) {
  545.             short theBool = *((short *)inPtr);
  546.             short index;
  547.             Str32 theText;
  548.             index = theBool ? kTrueWord : kFalseWord;
  549.             GetIndString(theText, kGeneralStrings, index);
  550.             myErr = AECreateDesc(typeChar, (Ptr)&theText[1], theText[0], result);
  551.         } else {
  552.             myErr = errAECoercionFail;
  553.         }
  554.     }
  555.     return(myErr);
  556. }
  557.  
  558. /*  CoerceQDRectToChar is the same kinda thing */
  559. pascal OSErr CoerceQDRectToChar(DescType origData, Ptr inPtr, Size theSize, DescType toType, long refCon, AEDesc *result)
  560. {
  561. #pragma unused (theSize,refCon)
  562.     OSErr myErr = noErr;
  563.     /* make sure everything is fine first */
  564.     if (origData != typeQDRectangle || toType != typeChar) {
  565.         myErr = errAECoercionFail;
  566.     } else {
  567.         short *myRect = (short *)inPtr;
  568.         register qq;
  569.         Str255 theString;
  570.         Str32 tempString;
  571.         Str32 spaceString = "\p ";
  572.         
  573.         theString[0] = 0;
  574.         GetIndString(theString, kGeneralStrings, kSayRectangle);
  575.         for (qq = 0; qq < kFour; qq++) {
  576.             NumToString(*myRect, tempString);
  577.             AppendString(theString, tempString);
  578.             AppendString(theString, spaceString);
  579.             myRect = myRect + 1;                            /* I have had trouble with MPW C doing a += on a pointer, so I'll do it this way */
  580.         }
  581.         myErr = AECreateDesc(typeChar, (Ptr)&theString[1], theString[0], result);
  582.     }
  583.     return(myErr);
  584. }
  585.  
  586. /* these next two I really do find useful, dealing with Toolbox strings */
  587. pascal OSErr CoerceCharToPString(DescType origData, Ptr inPtr, Size theSize, DescType toType, long refCon, AEDesc *result)
  588. {
  589. #pragma unused (origData,toType,refCon)
  590.     OSErr myErr = noErr;
  591.     Str255 theString;
  592.     theString[0] = theSize;
  593.     BlockMove(inPtr, (Ptr)&theString[1], theSize);
  594.     myErr = AECreateDesc(typeMyPString, (Ptr)&theString[0], theString[0] + 1, result);
  595.     
  596.     return(myErr);
  597. }
  598.  
  599. pascal OSErr CoercePStringToChar(DescType origData, Ptr inPtr, Size theSize, DescType toType, long refCon, AEDesc *result)
  600. {
  601. #pragma unused (origData,toType,refCon)
  602.     OSErr myErr = noErr;
  603.     myErr = AECreateDesc(typeChar, (Ptr)(inPtr + 1), theSize - 1, result);
  604.     
  605.     
  606.     return(myErr);
  607. }
  608.  
  609. /* CoerceAliasToTargetID takes an applicaiton alias and coerces it to a process target ID */
  610. /* Of course, to do this is needs to find and launch the application */
  611. /* This handler uses pointers to the data, since the AppleEvent managr can handle this type */
  612. /* of manipulation more efficiently than passing descs.  You can install a desc handler */
  613. /* instead, if you'd like. */
  614. /* I actually don't use this in this sample, but I left it in as an example */
  615. /* of a coercion handler */
  616. pascal OSErr CoerceAliasToTargetID(DescType origData, Ptr inPtr, Size theSize, DescType toType, long refCon, AEDesc *returnID)
  617. {
  618. #pragma unused (origData,toType,refCon)
  619.     OSErr myErr;
  620.     LaunchParamBlockRec launchThis;
  621.     FSSpec theSpec;
  622.     Boolean changed;
  623.     Handle theAlias = NewHandle(theSize);
  624.     HLock(theAlias);
  625.     BlockMove(inPtr, (Ptr)*theAlias, theSize);
  626.     HUnlock(theAlias);
  627.     launchThis.launchAppSpec = &theSpec;
  628.     /* the caller may have already done this, but it doesn't hurt to do it again */
  629.     myErr = ResolveAlias(nil, (AliasHandle)theAlias, launchThis.launchAppSpec, &changed);
  630.     if (myErr)
  631.         return(myErr);
  632.     /* launch the thing */
  633.     launchThis.launchBlockID = extendedBlock;
  634.     launchThis.launchEPBLength = extendedBlockLen;
  635.     launchThis.launchFileFlags = nil;
  636.     /* launchdontswitch because we just want to use the service.  Also, it may be a */
  637.     /* background only application, so like you don't want it to come up, y'know? */
  638.     launchThis.launchControlFlags = launchContinue + launchNoFileFlags + launchDontSwitch;
  639.     launchThis.launchAppParameters = nil;
  640.     myErr = LaunchApplication(&launchThis);
  641.     if (myErr) {
  642.         mAEErrorDisplay("\pLaunchApplication error", myErr)
  643.         return(myErr);
  644.     }
  645.     /* it launched.  the PSN has been stored in the launchProcessSN field, now we have to make */
  646.     /* that a target */
  647.     /* fill in all the details for the target */
  648.     /* we'll just use the PSN to communicate */
  649.     myErr = AECreateDesc(typeProcessSerialNumber, (Ptr)&launchThis.launchProcessSN, sizeof(ProcessSerialNumber), returnID);
  650.     mAEErrorDisplay("\pCreateDesc error after launch", myErr)
  651.     return(myErr);
  652. }
  653.  
  654.  
  655. #undef __AEM__
  656.